package pers.muci.flowable.step1;

import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.cfg.StandaloneInMemProcessEngineConfiguration;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.junit.Before;
import org.junit.Test;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ClassName: CreateProcessEngine
 * Description: flowable 的基本使用
 * date: 2022/1/12 10:49
 *
 * @author muci
 * @since JDK 1.8
 */
public class FlowableSimpleUseTest {
    private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    /**
     * Table act_ge_property doesn't exist 问题解决  nullCatalogMeansCurrent=true
     * 原因是，在该mysql中其他 schema 中存在 act_ge_property 表，创建时 默认该表已经存在。
     */
    private static final String URL = "jdbc:mysql://106.14.15.56:3306/flowable?nullCatalogMeansCurrent=true";
    private static final String USER = "root";
    private static final String PASSWORD = "root";
    private ProcessEngineConfiguration processEngineConfiguration;

    /**
     * 初始化流程引擎
     */
    @Before
    public void createProcessEngine() {
        //StandaloneInMemProcessEngineConfiguration 创建的引擎是不在spring环境中的，是独立使用的
        processEngineConfiguration = new StandaloneInMemProcessEngineConfiguration()
                .setJdbcDriver(DRIVER)
                .setJdbcUrl(URL)
                .setJdbcUsername(USER)
                .setJdbcPassword(PASSWORD)
                // 当数据库中不存在所需表时，会自动创建
                .setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);

        ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
    }

    /**
     * 部署流程定义
     * 流程定义格式：BPMN2.0,
     * 一个流程定义可以启动多个流程实例。
     * 过程：
     * 流程引擎将XML文件存储在数据库中，可以在需要时获取它。
     * 流程定义转换成内部的、可执行的对象模型，即使用它时可以启动流程实例。
     */
    @Test
    public void deployPrecessDefinition() {
//        RepositoryService 类，提供对流程定义和部署存储库的访问的服务。
        RepositoryService repositoryService = processEngineConfiguration.getRepositoryService();
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("bpmn/holiday-request.bpmn20.xml")
                .deploy();

        System.out.println("id:  " + deploy.getId());
        //根据流程部署后的id，进行查询，并获取流程定义的name，， singleResult() 获取单个流程定义
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploy.getId()).singleResult();
        System.out.println("Found process definition : " + processDefinition.getName());

    }

    /**
     * 启动流程实例
     * 对于拥有流程变量的流程定义来说，再启动时应初始化这些变量，，，这里演示时是写死的。
     * 如何启动你要启动的那个流程定义，ACT_RE_PROCDEF 表中有一个 KEY_ 字段，这个字段存储的就是 XML 文件设置的id，即 holidayRequest，
     * 流程实例启动后，会创建一个 执行（execution），并将其放在启动事件上，这个执行沿着顺序流进行。
     * 启动流程实例，会在数据库中创建一个任务。
     * <p>
     * 事务：默认情况下，flowables所有的操作都是同步的、处于同一个事务下。即 流程启动后，
     **/
    @Test
    public void startProcessInstance() {
        RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
//        初始化流程变量
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("employee", "employeeTest");
        variables.put("nrOfHolidays", "nrOfHolidaysTest");
        variables.put("description", "descriptionTest");
//        启动一个流程实例,第二个参数是设置业务主键，工作流主要是用于协作系统业务流程的实现，所以必须和业务流程绑定
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holidayRequest", "1111", variables);

        System.out.println("processInstance.getBusinessKey() = " + processInstance.getBusinessKey());
        System.out.println("processInstance.getId() = " + processInstance.getId());
    }

    /**
     * 查询与完成任务
     * 实际应用中，例如请假流程，会根据用户id，去查询请假的业务表，根据这个业务表的主键查询流程进度（即任务）
     */
    @Test
    public void queryTask() {
        TaskService taskService = processEngineConfiguration.getTaskService();
        TaskQuery taskQuery = taskService.createTaskQuery();
//        获取当前任务
        Task approveTask = taskQuery.taskDefinitionKey("approveTask").singleResult();
        System.out.println(approveTask.getTaskDefinitionKey());
        System.out.println("approveTask.getName() = " + approveTask.getName());
        //获取流程变量
        Map<String, Object> processVariables = approveTask.getProcessVariables();
        System.out.println("processVariables.get(\"employee\") = " + processVariables.get("employee"));

        //完成任务,指定 该任务的完成人,,,此时需要创建 CallExternalSystemDelegate 类
        HashMap<String, Object> map = new HashMap<>();
        map.put("approved", true);
        taskService.complete(approveTask.getId(), map);
    }

    /**
     * 查询历史数据，
     * Flowable 会自动存储所有流程实例的 审计数据 或 历史数据
     */
    @Test
    public void queryHistoryData() {
        HistoryService historyService = processEngineConfiguration.getHistoryService();
        List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey("1111").list();
        for (HistoricProcessInstance instance : list) {
            System.out.println(instance.getName());
        }
    }
}